const _doc: any = document;
const _docEl: any = _doc.documentElement;

/**
 * 获取滚动条宽度（避免滚动条出现/消失，页面挤压）
 */
export const getScrollBarSize = (scrollBarWidth: any) => {
  if (scrollBarWidth !== undefined) {
    return scrollBarWidth;
  }

  const outer = document.createElement('div');
  outer.style.visibility = 'hidden';
  outer.style.width = '100px';
  outer.style.position = 'absolute';
  outer.style.top = '-9999px';
  document.body.appendChild(outer);

  const widthNoScroll = outer.offsetWidth;
  outer.style.overflow = 'scroll';

  const inner = document.createElement('div');
  inner.style.width = '100%';
  outer.appendChild(inner);

  const widthWithScroll = inner.offsetWidth;
  outer?.parentNode?.removeChild(outer);
  scrollBarWidth = widthNoScroll - widthWithScroll;

  return scrollBarWidth;
};

/**
 * 生成弹窗随机ID
 */
export const generateId = () => {
  return Math.floor(Math.random() * 10000);
};

type IScroll = 'top' | 'left' | 'width' | 'height';

export const convert = (str: string) => {
  const c = str.charAt(0);
  return c.toUpperCase() + str.replace(c, '');
};

export const scroll = (type: IScroll) => {
  const _s = convert(type);
  return _docEl['scroll' + _s] || _doc.body['scroll' + _s];
};

export const client = (type: IScroll) => {
  const _s = convert(type);
  return _docEl['client' + _s] || _doc.body['client' + _s];
};

export const getFollowRect = (follow: any, oW: any, oH: any) => {
  let followo;
  let t = typeof follow;
  let rT, rL, sT, sL, _top, _left, _placement;
  let wW = client('width');
  let wH = client('height');

  // 定位元素（class或id .xxx|#xxx）
  if (t == 'string') {
    followo = document.querySelector(follow);
    rT = Math.round(followo.getBoundingClientRect().top);
    rL = Math.round(followo.getBoundingClientRect().left);
    sT = rT + scroll('top');
    sL = rL + scroll('left');
    if (rT + oH + followo.offsetHeight > wH) {
      _top = sT - oH;
      _placement = 'top';
    } else {
      _top = sT + followo.offsetHeight;
      _placement = 'bottom';
    }
    _left = rL + oW > wW ? sL - oW + followo.offsetWidth : sL;
  }
  // 定位元素（坐标点 clientX|clientY）
  if (t == 'object') {
    rT = follow[1];
    rL = follow[0];
    sT = rT + scroll('top');
    sL = rL + scroll('left');
    _top = rT + oH > wH ? sT - oH : sT;
    _left = rL + oW > wW ? sL - oW : sL;
  }

  return [_left, _top, _placement];
};

// 获取弹窗最大层级
export const getZIndex = (zIndex: number) => {
  let idx = zIndex,
    elem: any = document.getElementsByTagName('*'),
    i = 0,
    len = elem.length;

  for (; i < len; i++) {
    idx = Math.max(idx, elem[i].style.zIndex);
  }
  return idx;
};

// 获取样式
export const getStyle = (el: any, attr: string) => {
  if (!el || !attr) {
    return;
  }

  return el.currentStyle
    ? el.currentStyle[attr]
    : _doc.defaultView.getComputedStyle(el, null)[attr];
};

export function getByClass(sClass: string, oParent: HTMLElement | Document) {
  var parent = oParent || document;
  var aEles = parent.getElementsByTagName('*');
  var re = new RegExp('\\b' + sClass + '\\b');
  var arr = [];

  for (var i = 0; i < aEles.length; i++) {
    if (re.test(aEles[i].className)) {
      arr.push(aEles[i]);
    }
  }

  return arr;
}

export const on = (el: any, event: any, handle: any) => {
  el.attachEvent
    ? el.attachEvent('on' + event, handle)
    : el.addEventListener(event, handle, false);
};

export const off = (el: any, event: any, handle: any) => {
  el.detachEvent
    ? el.detachEvent('on' + event, handle)
    : el.removeEventListener(event, handle, false);
};

export const classNames = (...args: any[]) => {
  const hasOwn = {}.hasOwnProperty;
  const classes = [];

  for (let i = 0; i < args.length; i++) {
    const item = args[i];

    if (!item) {
      continue;
    }

    const argType = typeof item;

    if (argType === 'string' || argType === 'number') {
      classes.push(item);
    } else if (Array.isArray(item) && item.length) {
      const inner: string = classNames.apply(null, item);
      if (inner) {
        classes.push(inner);
      }
    } else if (argType === 'object') {
      for (const key in item) {
        if (hasOwn.call(item, key) && item[key]) {
          classes.push(key);
        }
      }
    }
  }
  return classes.join(' ');
};

export function getCss(el: any, attr: string) {
  let value = getStyle(el, attr);
  return removeUnit(value);
}

export function setCss(el: any, attr: string, val: any) {
  let reg =
    /^(width|height|top|bottom|left|right|(margin|padding)(Top|Left|Bottom|Right)?)$/;

  if (!isNaN(val) && reg.test(attr)) {
    el.style[attr] = val + 'px';
    return;
  }

  el.style[attr] = val;
}

export function removeUnit(value: any) {
  let reg = /^[-+]?([1-9]\d+|\d)(\.\d+)?(px|pt|em|rem|vh|vw)$/;

  if (isNaN(value)) {
    if (reg.test(value)) {
      return parseFloat(value);
    }
    return Number(value);
  }

  return value;
}

export function debounce<A = unknown, R = void>(
  fn: (args: A) => R,
  ms: number,
): [(args: A) => Promise<R>, () => void] {
  let timer: NodeJS.Timeout;

  const debounceFunc = (args: A): Promise<R> =>
    new Promise((resolve) => {
      if (timer) {
        clearTimeout(timer);
      }
      timer = setTimeout(() => {
        resolve(fn(args));
      }, ms);
    });

  const clearDebounce = () => clearTimeout(timer);
  return [debounceFunc, clearDebounce];
}

/** 判断是否是dom节点 */
export const isDOM = (obj: any) => {
  if (typeof HTMLElement === 'object') {
    return obj instanceof HTMLElement;
  }

  return (
    obj &&
    typeof obj === 'object' &&
    obj.nodeType === 1 &&
    typeof obj.nodeName === 'string'
  );
};
